﻿// <copyright file="BubblesVisual.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2009-04-18</date>
// <summary>OpenWPFChart library. Bubbles Visual draws the set of 3D points as bubbles in 2D space.</summary>
// <revision>$Id$</revision>

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Media;

namespace OpenWPFChart.Parts
{
	/// <summary>
	/// BubblesVisual draws the set of 3D points as bubbles in 2D space.
	/// </summary>
	public class BubblesVisual : ItemVisual
	{
		/// <summary>
		/// Renders the bubbles.
		/// </summary>
		protected internal override void Render()
		{
			Children.Clear();

			using (DrawingContext dc = RenderOpen())
			{
				BubblePointsDataView dataView = DataView as BubblePointsDataView;
				if (dataView == null)
					return;
				IPoint3DSeries pointSeries = dataView.ItemData as IPoint3DSeries;
				if (pointSeries == null)
					return;

				ChartScale hScale = dataView.HorizontalScale, vScale = dataView.VerticalScale
					, zScale = dataView.ZScale;
				if (hScale == null || !hScale.CompatibleWith(dataView.ItemData.GetDimensionType(0))
					|| vScale == null || !vScale.CompatibleWith(dataView.ItemData.GetDimensionType(1))
					|| zScale == null || !zScale.CompatibleWith(dataView.ItemData.GetDimensionType(2)))
					return;

				// Chart area size.
				Size areaSize = new Size(hScale.ToPixels(hScale.Stop), vScale.ToPixels(vScale.Stop));

				Typeface labelTypeface = new Typeface(dataView.FontFamily, dataView.FontStyle
					, dataView.FontWeight, dataView.FontStretch);
				string labelFormat = "{0:" + dataView.LabelFormat + "}";

				// Loop by points
				foreach (var pt in pointSeries.Points)
				{
					double x, y;
					if (dataView.Orientation == Orientation.Horizontal)
					{
						x = hScale.ToPixels(pt.X);
						y = vScale.ToPixels(pt.Y);
						if (!isInsideArea(new Point(x, y), areaSize))
							continue;
					}
					else // Orientation == Orientation.Vertical
					{
						y = hScale.ToPixels(pt.X);
						x = vScale.ToPixels(pt.Y);
						if (!isInsideArea(new Point(y, x), areaSize))
							continue;
					}

					// Bubble.
					Drawing bubble = dataView.Bubble.Clone();
					
					// Bubble trasformation to change its size according to the Z value and
					// position according to x,y values.
					double z = zScale.ToPixels(pt.Z);
					double scale = z / Math.Sqrt(bubble.Bounds.Width * bubble.Bounds.Width
						+ bubble.Bounds.Height * bubble.Bounds.Height);
					ScaleTransform st = new ScaleTransform(scale, scale);
					TranslateTransform tt = new TranslateTransform(x, y);
					Transform transform = null;
					GeometryDrawing geometryBubble = bubble as GeometryDrawing;
					if (geometryBubble != null)
					{
						geometryBubble.Geometry.Transform = st;
						transform = tt;
					}
					else
					{
						TransformGroup tg = new TransformGroup();
						tg.Children.Add(st);
						tg.Children.Add(tt);
						transform = tg;
					}

					// Bubble Visual.
					bubble.Freeze();
					PointVisual bubbleVisual = new PointVisual(bubble);
					bubbleVisual.Transform = transform;

					Children.Add(bubbleVisual);

					// Label.
					if (dataView.LabelAlignment != BubbleLabelAlignment.DontShow)
					{
						string label = string.Format(labelFormat, pt.Z);
						FormattedText ftLabel = new FormattedText(label
							, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight
							, labelTypeface, dataView.FontSize, dataView.LabelBrush);

						DrawingVisual labelVisual = new DrawingVisual(); 
						using (DrawingContext lblCtx = labelVisual.RenderOpen())
						{
							lblCtx.DrawText(ftLabel, new Point(0, 0));
						}

						// TODO Implement ScaleLabelToBubbleSize property usage.

						switch (dataView.LabelAlignment)
						{
							case BubbleLabelAlignment.Center:
								labelVisual.Transform = new TranslateTransform(x - ftLabel.Width / 2
									, y - ftLabel.Height / 2);
								break;
							case BubbleLabelAlignment.Top:
								labelVisual.Transform = new TranslateTransform(x - ftLabel.Width / 2
									, y - bubble.Bounds.Height / 2 - ftLabel.Height);
								break;
							case BubbleLabelAlignment.Left:
								labelVisual.Transform = new TranslateTransform(x - bubble.Bounds.Width / 2
									- ftLabel.Width, y - ftLabel.Height / 2);
								break;
							case BubbleLabelAlignment.Bottom:
								labelVisual.Transform = new TranslateTransform(x - ftLabel.Width / 2
									, y + bubble.Bounds.Height / 2);
								break;
							case BubbleLabelAlignment.Right:
								labelVisual.Transform = new TranslateTransform(x + bubble.Bounds.Width / 2
									, y - ftLabel.Height / 2);
								break;
						}

						Children.Add(labelVisual);
					}
				}
			}
		}
	}
}
